package store.lunangangster.nodeflow.parser.base.expression;

import static store.lunangangster.nodeflow.enums.FlowType.FLOW_FOR;
import static store.lunangangster.nodeflow.enums.FlowType.FLOW_SWITCH;
import static store.lunangangster.nodeflow.enums.FlowType.FLOW_WHILE;
import static store.lunangangster.nodeflow.enums.NodeType.NODE_FOR;
import static store.lunangangster.nodeflow.enums.NodeType.NODE_SWITCH;
import static store.lunangangster.nodeflow.enums.NodeType.NODE_WHILE;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import java.util.ArrayList;
import java.util.List;
import store.lunangangster.nodeflow.builder.FlowBuilder;
import store.lunangangster.nodeflow.builder.NodeBuilder;
import store.lunangangster.nodeflow.common.BaseConstant;
import store.lunangangster.nodeflow.core.component.ForComponent;
import store.lunangangster.nodeflow.core.component.IfComponent;
import store.lunangangster.nodeflow.core.component.SwitchComponent;
import store.lunangangster.nodeflow.core.component.WhileComponent;
import store.lunangangster.nodeflow.enums.FlowType;
import store.lunangangster.nodeflow.enums.NodeType;
import store.lunangangster.nodeflow.exception.ExecutableItemNotFoundException;
import store.lunangangster.nodeflow.exception.RuleSourceParseException;
import store.lunangangster.nodeflow.exception.IllegalFlowExpressionException;
import store.lunangangster.nodeflow.rt.FlowBus;
import store.lunangangster.nodeflow.rt.element.Executable;
import store.lunangangster.nodeflow.rt.element.Node;
import store.lunangangster.nodeflow.rt.element.flow.Flow;

/**
 * 针对Expression式的Flow的解析辅助
 * @author cwk
 * @version 1.0
 */
public class ExpressionParserHelper {


	/**
	 * 解析节点标签中的value字段，匹配FlowBus中相应的Node
	 * <p>
	 *   该方法会按照','对value进行分割，可以自动去除空格。其实这里等价于{@link NodeType#NODE_COMMON}的pattern属性，
	 * 	 只不过是一个事物的两种描述：
	 * 	 <ol>
	 * 	   <li>这里更偏向于解决实际问题，快速地解析出用','分割的部分</li>
	 * 	   <li>而NODE_COMMON侧重于普通Node表达式的描述，即一个抽象的Node应该符合怎样的表达式规则</li>
	 * 	 </ol>
	 * @param value 待解析字符串
	 * @return 解析出的可执行对象列表
	 */
	public static List<Executable> resolveValue(String value) {
		return resolveValue(value, "\\s*,\\s*");
	}

	/**
	 * 解析节点标签中，某些Expression表达式的子表达式，匹配FlowBus中相应的Node
	 * <p>
	 *   该方法会按照';'对value进行分割，可以自动去除空格。
	 *   通常用于对Expression表达式对子表达式，如：while(node) {subExpression}、loop(subExpression)
	 * @return 解析出的可执行对象列表
	 * </p>
	 */
	private static List<Executable> resolveSubValue(String value) {
		return resolveValue(value, "\\s*;\\s*");

	}

	private static List<Executable> resolveValue(String value, String regex) {
		if (StrUtil.isBlank(value)) {
			return null;
		}
		// 按照','进行分割，会自动去除空白
		// 这里的trim()是必须的，因为如果value的开头有空白，expressions中就会产生一个空白
		String[] expressions = value.trim().split(regex);
		List<Executable> executableList = new ArrayList<>();
		for (String expression : expressions) {
			// 解析出Node or Flow or Chain
			Executable executable = ExpressionNode.resolve(expression);
			if (executable == null) {
				throw new ExecutableItemNotFoundException(String.format("Find no available CommonNode or Chain for expression: %s", expression));
			}
			executableList.add(executable);
		}
		return executableList;
	}

	/**
	 * 判断expression是否是nodeType类型的表达式
	 * @param expression Flow表达式
	 * @param nodeType   指定NodeType
	 * @return 当满足条件时返回true
	 */
	public static boolean isNodeExpression(String expression, NodeType nodeType) {
		if (nodeType == null) {
			return false;
		}
		return nodeType.valuePattern.matcher(expression).matches();
	}

	/**
	 * 解析NODE_COMMON类型或者子流程的expression，没有进行expression检查，默认：true == isNodeExpression(expression, NODE_COMMON)
	 * <p>
	 *   CommonExpression是一个普通的、不包含','、';'、'|'等特殊符号的字符串表达式，是{@link Node#getId()}
	 *
	 * @param expression	Flow表达式
	 * @return 解析出的普通可执行对象
	 * @throws ExecutableItemNotFoundException	找不到表达式对应的Node or Chain
	 */
	public static Executable resolveCommonExpression(String expression)
			throws ExecutableItemNotFoundException {
		Executable executable = FlowBus.getExecutable(expression);
		if (executable == null) {
			throw new ExecutableItemNotFoundException(String.format("Find no available CommonNode or Chain for expression: %s", expression));
		}
		return executable;
	}

	/**
	 * 解析NODE_IF类型的expression，没有进行expression检查，默认：true == isNodeExpression(expression, NODE_IF)
	 * <p>
	 *   IfExpression一般具有以下形式：ifNode(trueBranch | falseBranch)。
	 *   其中：
	 *   <ul>
	 *     <li>ifNode是一个{@link Node}，并且<code>{@link Node#getNodeComponent()} instanceof {@link IfComponent}</code>。ifNode是必须的</li>
	 *     <li>trueBranch是一个普通的{@link Flow}。trueBranch并非必须的</li>
	 *     <li>falseBranch是一个普通的{@link Flow}。falseBranch并非必须的</li>
	 *   </ul>
	 *
	 * @param expression	Flow表达式
	 * @return 返回解析出的IfFlow
	 * @throws ExecutableItemNotFoundException	找不到表达式对应的Node or Chain
	 * @throws IllegalFlowExpressionException		表达式错误，解析失败
	 */
	static Executable resolveIfExpression(String expression)
			throws ExecutableItemNotFoundException, IllegalFlowExpressionException {
		String[] split = expression.split("[\\s*()|]+");
		if (split.length <= 1) {
			throw new IllegalFlowExpressionException(String.format("Illegal flow expression as %s: \"%s\", IfExpression should like: \"ifNode(trueBranch | falseBranch) or ifNode(trueBranch)\"",
					NodeType.NODE_IF.name(), expression));
		}
		List<Executable> executableList = new ArrayList<>(3);
		// 获取条件节点，条件节点要单独构造Flow
		Executable executable = FlowBus.getExecutable(split[0]);
		if (!(executable instanceof Node)) {
			throw new ExecutableItemNotFoundException(String.format("Find no available IfNode for expression: \"%s\"", expression));
		}
		Node ifNode = (Node) executable;
		// 保证条件节点一定要是IfNodeComponent
		if (!(NodeType.NODE_IF.equals(ifNode.getType()))) {
			throw new IllegalFlowExpressionException(String.format("Illegal flow expression as a IfNode: \"%s\"", split[0]));
		}
		// true分支
		Executable trueBranch = FlowBus.getExecutable(split[1]);
		// false分支
		Executable falseBranch = split.length < 3 ? null : FlowBus.getExecutable(split[2]);
		executableList.add(ifNode);
		executableList.add(trueBranch);
		if (falseBranch != null) {executableList.add(falseBranch);}
		if (trueBranch == null) {
			throw new ExecutableItemNotFoundException(String.format("Find no available IfNode for expression: \"%s\"", expression));
		}
		return FlowBuilder.buildFlow(FlowType.FLOW_IF, executableList, null, true, false);
	}

	/**
	 * 解析NODE_FOR类型的expression，没有进行expression检查，默认：true == isNodeExpression(expression, NODE_FOR)
	 * <p>
	 *   ForExpression一般具有以下形式：for(forNode; forBody)。
	 *   其中：
	 *   <ul>
	 *     <li>forNode必须是一个{@link Node}，并且<code>{@link Node#getNodeComponent()} instanceof {@link ForComponent}</code>。forNode必须存在</li>
	 *     <li>forBody是一个普通的{@link Flow}。forBody并非是必须的</li>
	 *   </ul>
	 * <p>ForExpression中不支持使用ExitNode，可以在ForNode中添加ExitComponent逻辑，效果是几乎相同的
	 * <p>如果非要在ForNode中使用ExitNode，请使用完整的for标签形式，并且添加"exit"属性
	 *
	 * @param expression	Flow表达式
	 * @return 返回解析出的ForFlow
	 * @throws ExecutableItemNotFoundException	找不到表达式对应的Node or Chain
	 * @throws IllegalFlowExpressionException		表达式错误，解析失败
	 */
	static Executable resolveForExpression(String expression)
			throws ExecutableItemNotFoundException, IllegalFlowExpressionException {
		String[] split = expression.split("[\\s*();]+");
		if (split.length < 3 || !BaseConstant.FOR.equals(split[0])) {
			throw new IllegalFlowExpressionException(String.format("Illegal flow expression as %s: \"%s\", ForExpression should like: \"for(n; forBody) or for(forNode; forBody)\"",
					NODE_FOR.name(), expression));
		}
		// split[1]可能是int loopTimes || Node forNode
		Node forNode;
		if (FlowBus.getExecutable(split[1]) != null) {
			Executable executable = FlowBus.getExecutable(split[1]);
			boolean illegal;
			if (executable instanceof Node) {
				Node node = (Node) executable;
				// 验证是否是LoopComponent
				illegal = NODE_FOR.equals(node.getType()) && node.isComponent(ForComponent.class);
			} else {
				illegal = false;
			}
			if (!illegal) {
				throw new IllegalFlowExpressionException(String.format("Illegal flow expression as %s: \"%s\", ForExpression should like: \"for(n; forBody) or for(forNode; forBody)\"",
						NODE_FOR.name(), expression));
			}
			forNode = (Node) executable;
		} else {
			int loopTimes;
			try {
				loopTimes = Integer.parseInt(split[1]);
			} catch (NumberFormatException e) {
				throw new ExecutableItemNotFoundException(String.format("Find no available ForNode for expression: \"%s\", ForExpression should like: \"for(n; forBody) or for(forNode; forBody)\"", expression));
			}
			// 创建一个临时的LoopComponent
			ForComponent fixedLoopComp = ForComponent.getFixedLoopComponent(loopTimes);
			// 在解析Expression时生成Node的实例，这个Node不会被添加到FlowBus中，方便GC
			forNode = NodeBuilder.start()
					.id(null)
					.component(fixedLoopComp)
					.clazz(ForComponent.class)
					.type(NODE_FOR)
					.build(false);
		}
		List<Executable> executableList = CollUtil.toList(forNode);
		List<Executable> loopBody = ExpressionParserHelper.resolveValue(split[2]);
		if (loopBody != null) {
			executableList.addAll(loopBody);
		}
		return FlowBuilder.buildFlow(FLOW_FOR, executableList, null, true, false);
	}

	/**
	 * 解析NODE_WHILE类型的expression，没有进行expression检查，默认：true == isNodeExpression(expression, NODE_WHILE)
	 * <p>
	 *   WhileExpression一般具有以下形式：while(whileNode) {whileBody}。
	 *   其中：
	 *   <ul>
	 *     <li>whileNode是一个{@link Node}，并且<code>{@link Node#getNodeComponent()} instanceof {@link WhileComponent}。whileNode是必须的</code></li>
	 *     <li>whileBody是一个普通的{@link Flow}。whileBody并非是必须的</li>
	 *   </ul>
	 * <p>WhileExpression不支持使用ExitNode，可以在WhileNode中添加ExitComponent逻辑，效果是几乎相同的
	 * <p>如果非要在WhileNode中使用ExitNode，请使用完整的while标签形式，并且添加"exit"属性
	 *
	 * @param expression	Flow表达式
	 * @return 返回解析出的WhileFlow
	 * @throws ExecutableItemNotFoundException	找不到表达式对应的Node or Chain
	 * @throws IllegalFlowExpressionException		表达式错误，解析失败
	 */
	static Executable resolveWhileExpression(String expression)
			throws ExecutableItemNotFoundException, IllegalFlowExpressionException {
		String[] split = expression.split("[\\s*(){}]+");
		if (split.length < 2 || !BaseConstant.WHILE.equals(split[0])) {
			throw new IllegalFlowExpressionException(String.format("Illegal flow expression as %s: \"%s\", WhileExpression should like: \"while(whileNode) {flow}\"",
					NODE_WHILE.name(), expression));
		}
		// split[1]应该是一个NODE_WHILE
		Executable executable = FlowBus.getExecutable(split[1]);
		if (executable == null) {
			throw new ExecutableItemNotFoundException(String.format("Find no available WhileNode for expression: \"%s\"", split[1]));
		}
		boolean illegal = false;
		if (executable instanceof Node) {
			Node whileNode = (Node) executable;
			if (NODE_WHILE.equals(whileNode.getType()) && whileNode.isComponent(WhileComponent.class)) {
				illegal = true;
			}
		}
		if (!illegal) {
			throw new IllegalFlowExpressionException(String.format("Illegal flow expression as %s: \"%s\", WhileExpression should like: \"while(whileNode) {flow}\"",
					NODE_WHILE.name(), expression));
		}
		List<Executable> executableList = CollUtil.toList(executable);
		String subFlow = expression.substring(expression.indexOf('{') + 1, expression.lastIndexOf('}'));
		List<Executable> whileBody = resolveSubValue(subFlow);
		if (!CollUtil.isEmpty(whileBody)) {
			executableList.addAll(whileBody);
		}
		return FlowBuilder.buildFlow(FLOW_WHILE, executableList, null, true, false);
	}

	/**
	 * 解析NODE_SWITCH类型的expression，没有进行expression检查，默认：true == isNodeExpression(expression, NODE_SWITCH)
	 * <p>
	 *   SwitchExpression一般具有以下形式：switch(switchNode) {node1|node2|node3|....}
	 *   其中：
	 *   <ul>
	 *     <li>SwitchNode是一个{@link Node}，并且<code>{@link Node#getNodeComponent()} instanceof {@link SwitchComponent}。SwitchNode是必须的</code></li>
	 *     <li>SwitchBody是由多个普通的{@link Node}组成的集合，并且switchNode的{@link SwitchComponent#processSwitch()}返回值必须是该集合中某个node的nodeId，否则会抛出{@link RuleSourceParseException}</li>
	 *   </ul>
	 *
	 * @param expression	Flow表达式
	 * @return 返回解析出的SwitchFlow
	 * @throws ExecutableItemNotFoundException	找不到表达式对应的Node or Chain
	 * @throws IllegalFlowExpressionException		表达式错误，解析失败
	 */
	static Executable resolveSwitchExpression(String expression)
			throws ExecutableItemNotFoundException, IllegalFlowExpressionException {
		String[] split = expression.split("[\\s*(){}]+");
		if (split.length < 2 || !BaseConstant.SWITCH.equals(split[0])) {
			throw new IllegalFlowExpressionException(String.format("Illegal flow expression as %s: \"%s\", SwitchExpression should like: \"switch(switchNode) {node0|node1|...}\"",
					NODE_SWITCH.name(), expression));
		}
		// split[1]应该是一个NODE_SWITCH
		Executable executable = FlowBus.getExecutable(split[1]);
		if (executable == null) {
			throw new ExecutableItemNotFoundException(String.format("Find no available SwitchNode for expression: \"%s\"", split[1]));
		}
		List<Executable> executableList;
		boolean illegal = false;
		if (executable instanceof Node) {
			Node switchNode = (Node) executable;
			if (NODE_SWITCH.equals(switchNode.getType()) && switchNode.isComponent(SwitchComponent.class)) {
				illegal = true;
			}
		}
		if (!illegal) {
			throw new IllegalFlowExpressionException(String.format("Illegal flow expression as %s: \"%s\", SwitchExpression should like: \"switch(switchNode) {node0|node1|...}\"",
					NODE_SWITCH.name(), expression));
		}
		String subFlow = expression.substring(expression.indexOf('{') + 1, expression.lastIndexOf('}'));
		if (StrUtil.isBlank(subFlow)) {
			// 在解析阶段就判断SwitchList是否为空
			throw new IllegalFlowExpressionException("Incompatible executable for SwitchFlow: SwitchList should not be empty: " + expression);
		} else {
			String[] subSplit = subFlow.split("\\|");
			executableList = new ArrayList<>(subSplit.length + 1);
			// switchNode在executableList[0]
			executableList.add(executable);
			for (String subExpression : subSplit) {
				Executable executableNode = resolveCommonExpression(subExpression.trim());
				if (!(executableNode instanceof Node)) {
					throw new IllegalFlowExpressionException(String.format("Illegal flow expression as %s: \"%s\", element in switchList should all be common Node",
							NODE_SWITCH.name(), expression));
				}
				executableList.add(executableNode);
			}
		}
		return FlowBuilder.buildFlow(FLOW_SWITCH, executableList, null, true, false);
	}
}
